﻿/* $RCSFile: PluginInstaller.cs $
 * Apache license v2 2011 by Christian Surieux
 * $Source: ?/CRMWrapper3/Plugins/PluginInstaller.cs $
 * $log$
 * Revision 6 2010/08/06 19:56:33  christian.surieux
 *   Added Log to All cs files
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

using CrmSdk;
using Microsoft.Crm.Workflow;
using AddonNice.CRMWrapper.CRM4Server;
using System.EnterpriseServices;
using System.IO;

using PropertyBag           =   Microsoft.Crm.Sdk.PropertyBag;
using AssemblySourceType    =   Microsoft.Crm.Sdk.AssemblySourceType;

namespace AddonNice.CRMWrapper.Plugins
{
    public class PluginInstaller
    {
        CrmWrapper4 Wrapper;

        public PluginInstaller(CRMWrapperBase Wrap)
        {
            Wrapper =   Wrap as CrmWrapper4;
        }

        private void DeployPlugin(string pluginAssemblyPath, string crmServer, string organizationName)
        {
            //Console.Write("Initializing CrmService... ");
            //CrmService crmService = CreateCrmService(crmServer, organizationName);
            //Console.WriteLine("Complete");

            pluginassembly pluginAssembly = LoadPluginAssembly(pluginAssemblyPath);

            UnregisterExistingSolution(pluginAssembly.name);

            SdkMessageProcessingStepRegistration[] steps = LoadPluginSteps(pluginAssemblyPath);

            Guid pluginAssemblyId = RegisterSolution(pluginAssembly, steps);

            plugintype[] workflowActivities = LoadWorkflowActivities(pluginAssemblyPath, pluginAssemblyId);
            RegisterWorkflowActivities(workflowActivities);
        }

        private plugintype[] LoadWorkflowActivities(string pluginAssemblyPath, Guid pluginAssemblyId)
        {
            List<plugintype> workflowActivities = new List<plugintype>();
            Assembly assembly                   = Assembly.LoadFile(pluginAssemblyPath);

            foreach (Type activityType in assembly.GetTypes())
            {
                CrmWorkflowActivityAttribute activityAttribute = 
                    (CrmWorkflowActivityAttribute)Attribute.GetCustomAttribute(
                    activityType, typeof(CrmWorkflowActivityAttribute));

                if (activityType.IsSubclassOf(typeof(Activity)) && activityAttribute != null)
                {
                    plugintype workflowActivity = new plugintype();
                    workflowActivity.typename = activityType.FullName;
                    workflowActivity.pluginassemblyid = new Lookup(){ type="pluginassembly", Value=pluginAssemblyId};
                    workflowActivity.isworkflowactivity = new CrmBoolean() { Value = true };
                    workflowActivity.friendlyname = activityAttribute.Name;
                    workflowActivities.Add(workflowActivity);
                }
            }

            return workflowActivities.ToArray();
        }

        private void RegisterWorkflowActivities(plugintype[] workflowActivities)
        {
            foreach (plugintype workflowActivity in workflowActivities)
            {
                Wrapper.Create(workflowActivity,true);
            }
        }

        private pluginassembly LoadPluginAssembly(string pluginAssemblyPath)
        {
            Assembly assembly = Assembly.LoadFile(pluginAssemblyPath);
            pluginassembly pluginAssembly = new pluginassembly();
            pluginAssembly.name = assembly.GetName().Name;
            pluginAssembly.sourcetype = new Picklist() { Value = AssemblySourceType.Database };
            pluginAssembly.culture = assembly.GetName().CultureInfo.ToString();
            pluginAssembly.version = assembly.GetName().Version.ToString();

            if (String.IsNullOrEmpty(pluginAssembly.culture))
            {
                pluginAssembly.culture = "neutral";
            }

            byte[] publicKeyToken = assembly.GetName().GetPublicKeyToken();
            StringBuilder tokenBuilder = new StringBuilder();
            foreach (byte b in publicKeyToken)
            {
                tokenBuilder.Append(b.ToString("x").PadLeft(2, '0'));
            }
            pluginAssembly.publickeytoken = tokenBuilder.ToString();

            pluginAssembly.content = Convert.ToBase64String(File.ReadAllBytes(pluginAssemblyPath));

            return pluginAssembly;
        }

        private void UnregisterExistingSolution(string assemblyName)
        {
            QueryByAttribute query = new QueryByAttribute();
            query.EntityName = EntityName.pluginassembly.ToString();
            query.ColumnSet = new ColumnSet() { Attributes = new string[] { "pluginassemblyid" } };
            query.Attributes = new string[] { "name" };
            query.Values = new object[] { assemblyName };

            RetrieveMultipleRequest request = new RetrieveMultipleRequest();
            request.Query = query;

            RetrieveMultipleResponse response;
            Console.Write("Searching for existing solution... ");
            response = (RetrieveMultipleResponse)Wrapper.adminExecuteRaw(request);
            Console.WriteLine("Complete");

            if (response.BusinessEntityCollection.BusinessEntities.Length > 0)
            {
                pluginassembly pluginAssembly = (pluginassembly)response.BusinessEntityCollection.BusinessEntities[0];
                Console.Write("Unregistering existing solution {0}... ", pluginAssembly.pluginassemblyid.Value);

                UnregisterSolutionRequest unregisterRequest = new UnregisterSolutionRequest();
                unregisterRequest.PluginAssemblyId = pluginAssembly.pluginassemblyid.Value;

                Wrapper.adminExecuteRaw(unregisterRequest);
                Console.WriteLine("Complete");
            }
        }

        private SdkMessageProcessingStepRegistration[] LoadPluginSteps(string pluginAssemblyPath)
        {
            List<SdkMessageProcessingStepRegistration> steps = new List<SdkMessageProcessingStepRegistration>();
            
            Assembly assembly = Assembly.LoadFile(pluginAssemblyPath);
            foreach (Type pluginType in assembly.GetTypes())
            {
                if (typeof(Microsoft.Crm.Sdk.IPlugin).IsAssignableFrom(pluginType) && !pluginType.IsAbstract)
                {
                    object[] stepAttributes = pluginType.GetCustomAttributes(typeof(PluginStepAttribute), false);
                    foreach (PluginStepAttribute stepAttribute in stepAttributes)
                    {
                        steps.Add(CreateStepFromAttribute(pluginType, stepAttribute));
                    }
                }
            }

            return steps.ToArray();
        }

        private SdkMessageProcessingStepRegistration CreateStepFromAttribute(Type pluginType, PluginStepAttribute stepAttribute)
        {
            SdkMessageProcessingStepRegistration step = new SdkMessageProcessingStepRegistration();
            step.Description = stepAttribute.Description;
            step.FilteringAttributes = stepAttribute.FilteringAttributes;
            step.InvocationSource = (int)stepAttribute.InvocationSource;
            step.MessageName = stepAttribute.Message;
            step.Mode = (int)stepAttribute.Mode;
            step.PluginTypeName = pluginType.FullName;
            step.PluginTypeFriendlyName = pluginType.FullName;
            step.PrimaryEntityName = stepAttribute.PrimaryEntityName;
            step.SecondaryEntityName = stepAttribute.SecondaryEntityName;
            step.Stage = (int)stepAttribute.Stage;
            step.SupportedDeployment = (int)stepAttribute.SupportedDeployment;

            if (String.IsNullOrEmpty(step.Description))
            {
                step.Description = String.Format("{0} {1} {2}", step.PrimaryEntityName, step.MessageName, stepAttribute.Stage);
            }

            if (!String.IsNullOrEmpty(stepAttribute.StepId))
            {
                List<SdkMessageProcessingStepImageRegistration> images = new List<SdkMessageProcessingStepImageRegistration>();
                object[] imageAttributes = pluginType.GetCustomAttributes(typeof(PluginImageAttribute), false);
                foreach (PluginImageAttribute imageAttribute in imageAttributes)
                {
                    if (imageAttribute.StepId == stepAttribute.StepId)
                    {
                        images.Add(CreateImageFromAttribute(imageAttribute));
                    }
                }

                if (images.Count > 0)
                {
                    step.Images = images.ToArray();
                }
            }

            return step;
        }

        private SdkMessageProcessingStepImageRegistration CreateImageFromAttribute(PluginImageAttribute imageAttribute)
        {
            SdkMessageProcessingStepImageRegistration image = new SdkMessageProcessingStepImageRegistration();
            
            if (!String.IsNullOrEmpty(imageAttribute.Attributes))
            {
                image.Attributes = imageAttribute.Attributes.Split(',');
            }
            
            image.EntityAlias = imageAttribute.EntityAlias;
            image.ImageType = (int)imageAttribute.ImageType;
            image.MessagePropertyName = imageAttribute.MessagePropertyName;

            return image;
        }

        private Guid RegisterSolution(pluginassembly pluginAssembly, SdkMessageProcessingStepRegistration[] steps)
        {
            RegisterSolutionRequest registerRequest = new RegisterSolutionRequest();
            registerRequest.PluginAssembly = pluginAssembly;
            registerRequest.Steps = steps;
            Console.Write("Registering solution... ");
            RegisterSolutionResponse registerResponse = 
                (RegisterSolutionResponse)Wrapper.adminExecuteRaw(registerRequest);
            Console.WriteLine("Complete");

            return registerResponse.PluginAssemblyId;
        }
    }
}
